package com.llcbenwu.authentication;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * 自定义成功处理器
 * @author lilinchun
 * @date 2021/10/25 0025 19:29
 */
@Component("llcAuthenticationSuccessHandler")
@Slf4j
//public class llcAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public class llcAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {


    @Autowired
    private ObjectMapper objectMapper;

    /**
     * spring 容器已经配置完成我们可以直接注入
     */
    @Resource
    private ClientDetailsService clientDetailsService;


    /**
     *
     */
    @Resource
    private AuthorizationServerTokenServices authorizationServerTokenServices;


    /**
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param authentication 封装请求的信息
     * @throws IOException
     * @throws ServletException
     */

    //登录成功会被调用
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                        HttpServletResponse httpServletResponse,
                                        Authentication authentication) throws IOException, ServletException {
        log.info("登录成功");
        // 获取头部属性 Authorization
        String header = httpServletRequest.getHeader("Authorization");

        if (header == null || !header.toLowerCase().startsWith("basic ")) {
            throw new UnapprovedClientAuthenticationException("请求头中无client信息");
        }

        //抽取解码头中的串
        String[] tokens = extractAndDecodeHeader(header, httpServletRequest);
        assert tokens.length == 2;

        String clientId= tokens[0];
        String clientSecret = tokens[1];

        /**
         * 获取clientDetails
         */
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);


        if (ObjectUtil.isEmpty(clientDetails)){
            throw new UnapprovedClientAuthenticationException("clientId不匹配:"+clientId);
        }else if(!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){
            throw new UnapprovedClientAuthenticationException("clientSecret不匹配:"+clientId);
        }
        //这里new TokenRequest时不用传
        /**
         *
         * Map<String, String> requestParameters   requestParameters去构建authentication 因为成功处理器中已经被传进来了所以这里设置我空
         * String clientId：这里是
         * Collection<String> scope, 这里是可以配置
         * String grantType 这里是4总登录模式中其中一种 而现在我们使用我们自定义cutom
         *
         */
        TokenRequest tokenRequest=new TokenRequest(MapUtil.empty(),clientId,clientDetails.getScope(),"cutom");

        /**
         * 创建OAuth2Request
         */
        OAuth2Request auth2Request=tokenRequest.createOAuth2Request(clientDetails);

        /**
         * 目前有了OAuth2Request 和Authentication现在可以拼接成
         */
        OAuth2Authentication   oAuth2Authentication=new OAuth2Authentication(auth2Request, authentication);


        /**
         * 获取令牌
         */
        OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
        //将封装的信息以json的格式返回
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        //将获取的令牌写回去
        httpServletResponse.getWriter().write(objectMapper.writeValueAsString(accessToken));

    }

    /**
     * 抽取解码头中的串
     * @param header
     * @param request
     * @return
     * @throws IOException
     */
    private String[] extractAndDecodeHeader(String header, HttpServletRequest request)
            throws IOException {

        byte[] base64Token = header.substring(6).getBytes("UTF-8");
        byte[] decoded;
        try {
            decoded = Base64.getDecoder().decode(base64Token);
        } catch (IllegalArgumentException e) {
            throw new BadCredentialsException(
                    "Failed to decode basic authentication token");
        }

        String token = new String(decoded, StandardCharsets.UTF_8);

        int delim = token.indexOf(":");

        if (delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        }
        return new String[]{token.substring(0, delim), token.substring(delim + 1)};
    }
}
